/*
 * Created on 2007-11-23
 *
 * TODO
 */
package jacky.lanlan.song.aop;

import jacky.lanlan.song.aop.advice.*;
import jacky.lanlan.song.closure.Judgment;
import jacky.lanlan.song.closure.ReturnableHandler;
import jacky.lanlan.song.reflection.ReflectionUtils;
import jacky.lanlan.song.reflection.proxy.ProxyFactory;
import jacky.lanlan.song.util.Logger;
import jacky.lanlan.song.util.cache.CacheUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aopalliance.aop.Advice;

/**
 * AOPContext的帮助工具类。
 * 
 * @author Jacky.Song
 */
public abstract class AOPUtils {
	
	private static final Logger logger=Logger.getLogger();
	
	/**
	 * Advice缓存，实现 Advice 的无状态Singleton语意。
	 * <p>
	 * key=Advice类型 value=对应的Advice
	 */
	private static final Map<Class<? extends Advice>,Advice> ADVICE_CACHE=
		new HashMap<Class<? extends Advice>,Advice> ();
	
	// Advice创建器，从 Advice class 反射创建 Advice 实例。Singleton，线程安全
	private static final ReturnableHandler<Class<? extends Advice>,Advice> ADVICE_CREATOR=
		new ReturnableHandler<Class<? extends Advice>,Advice> (){
			public Advice doWith(Class<? extends Advice> adviceClass) {
				try {
					return adviceClass.newInstance();// 通过反射创建advice实例
				}
				catch (Exception e) {
					logger.error("反射创建 Advice [" + adviceClass + "] 时出错", e);
					throw new RuntimeException(e);
				}
			}
	};
	
	/**
	 * 缓存并返回被缓存的Advice，每次访问该方法都会返回同一个对象。
	 * 
	 * @param adviceClass
	 *          需要缓存的Advice类
	 * @return 被缓存的对象
	 */
	private static Advice cacheAdvice(Class<? extends Advice> adviceClass) {
		return CacheUtils.returnFromCache(ADVICE_CACHE, adviceClass,ADVICE_CREATOR,adviceClass);
	}
	
	// 用log4j输出AdviceProcessor的错误日志Proxy
	private static AdviceProcessor createLogErrorAdviceProcessor(final AdviceProcessor adviceProcessor) {
		return ProxyFactory.createProxy(
				new InvocationHandler() {
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						try {
							return method.invoke(adviceProcessor, args);
						}
						catch (Exception e) {
							logger.error("拦截器 [" + args[1].getClass() + "] 拦截 " +
									args[2].getClass()+"."+((Method)args[3]).toGenericString()+
									" 方法时出错", e.getCause());
							throw new RuntimeException(e.getCause());
						}
					}
				},AdviceProcessor.class);
	}
	
	// 类实现方法缓存 key="实现类类名.方法签名" value=实现类方法，创建动机：查找实现方法需要大量反射，开销很高
	private static final Map<String,Method> CLASS_METHOD_CACHE = new HashMap<String,Method> ();

	// 提供Bean的实现接口的方法。Singleton，线程安全
	private static final ReturnableHandler<Object[],Method> BEAN_METHOD_PROVIDER=
		new ReturnableHandler<Object[],Method>() {
			public Method doWith(Object[] data) {
				Class<?> beanClass=(Class<?>) data[0];
				Method ifaceMethod=(Method) data[1];
				return ReflectionUtils.findMethod(
						beanClass, ifaceMethod.getName(), ifaceMethod.getParameterTypes());
			}
		};
	
	/**
	 * 查找、缓存然后返回Bean实现接口的方法。
	 * 
	 * @param beanClass
	 *          Bean实现类
	 * @param ifaceMethod
	 *          Bean实现的接口的方法
	 * @return 被缓存的Bean实现类方法
	 */
	private static Method findAndCacheBeanMethod(final Class<?> beanClass,final Method ifaceMethod) {
		String key=beanClass+"."+ifaceMethod.toGenericString();
		return CacheUtils.returnFromCache(CLASS_METHOD_CACHE, key,
				BEAN_METHOD_PROVIDER,new Object[]{beanClass,ifaceMethod});
	}
	
	/**
	 * 织入Advice到指定Bean对象，创建AOP Proxy。
	 * 
	 * @param canAdvise
	 *          判断方法是否需要拦截
	 * @param handler
	 *          处理方法advice
	 * @param bean
	 *          要实施advice的Bean
	 * @return 基于bean的、具有AOP功能的JDK Proxy
	 * @see AdviceHandler
	 */
	public static Object weavingObject(final Judgment<Method> canAdvise,final AdviceHandler handler,
			final Object bean) {
		logger.debug("开始为 [" + bean.getClass() + "] 创建 AOP Proxy...");
		
		return ProxyFactory.createProxy(bean,
				new InvocationHandler() {
					public Object invoke(Object proxy, final Method method, Object[] args) throws Throwable {
						// 注意，这里的method是接口中定义的方法，而我们想要的是这个obj的类的实现方法，所以需要用接口方法查找实现方法并缓存
						Method classMethod=findAndCacheBeanMethod(bean.getClass(),method);
						if(canAdvise.canPass(classMethod)) {
							return handler.adviseMethod(bean,classMethod,args);
						}
						return ReflectionUtils.invokeMethod(method, bean, args);// 其他方法不做advice，直接调用
					}
				});
	}
	
	/**
	 * 拦截对象方法(实施advice)。真正的AOP实现即是靠这个方法的。
	 * 
	 * @param adviceChain
	 *          拦截器链
	 * @param obj
	 *          目标对象
	 * @param pointcut
	 *          连接点(被拦截的方法)
	 * @param args
	 *          方法参数
	 * @return 调用pointcut后的返回值
	 * @throws Throwable
	 *           pointcut调用自身抛出的异常
	 */
	public static Object interceptMethod(AdviceChain adviceChain,
			Object obj,Method pointcut, Object... args) throws Throwable {
		// 优先调用 AroundAdvice
		Class<? extends AroundAdvice> aroundAdviceClass=adviceChain.getAroundAdvice();
		if (aroundAdviceClass != null && aroundAdviceClass != AroundAdvice.class) {// AroundAdvice.
																																							 // class是默认值
			logger.debug("发现 AroundAdvice，用" + aroundAdviceClass + "拦截方法 [" + pointcut.toGenericString() + "]");
			// 缓存 Advice
			Advice aroundAdvice=cacheAdvice(aroundAdviceClass);
			
			return AdviceProcessor.AROUND_ADVICE_PROCESSOR.processAdvice(null,// 不需要otherParam，传null
					aroundAdvice, obj, pointcut, args);
		}
		
		// 没有发现 AroundAdvice ，使用其他 Advice
		logger.debug("没有发现 AroundAdvice，用其他 Advice 拦截方法 [" + pointcut.toGenericString() + "]");
		
		/*
		if(logger.isDebugEnabled()) {
			logger.debug("开始拦截 ["+obj.getClass()+"]");
			logger.debug("方法 ["+pointcut.toGenericString()+"] 调用之前，激活 MethodBeforeAdvice 链:"+beforeAdvices);
		}
		 */
		
		// //////// 最先是 处理MethodBeforeAdvice链 //////////
		processAdviceChain(adviceChain.getMethodBeforeAdvices(), AdviceProcessor.BEFORE_ADVICE_PROCESSOR, null,// 不需要otherParam
																																																					 // ，
																																																					 // 传null
				obj, pointcut, args);
		
		/*
		if(logger.isDebugEnabled()) {
			logger.debug("调用被代理方法 ["+pointcut.toGenericString()+"]");
		}
		 */
		
		// //////// 然后第二步，调用Pointcut //////////
		Object returnValue=null;
		try {
			returnValue = pointcut.invoke(obj, args);
		}
		catch (Exception e) {
			
			/*
			if(logger.isDebugEnabled()) {
				logger.debug("方法 ["+pointcut.toGenericString()+"] 调用出现异常，激活 ThrowsAdvice 链:"+throwsAdvices);
			}
			 */
			
			// //////// 如果出现异常，处理ThrowsAdvice链 //////////
			// 由于是通过反射调用真正的方法，所以这里的e会被反射异常包装
			processAdviceChain(adviceChain.getThrowsAdvices(), AdviceProcessor.THROWS_ADVICE_PROCESSOR, e.getCause(),// 这里我们需要的是方法引发的异常
					obj, pointcut, args);
			// 调用完所有ThrowsAdvice后，重新抛出方法引发的异常(因为这是本来就应该抛出的)
			throw e.getCause();
		}
		finally {
			
			/*
			if(logger.isDebugEnabled()) {
				logger.debug("方法 ["+pointcut.toGenericString()+"] 执行完毕，激活 AfterAdvice 链:"+afterAdvices);
			}
			 */
			
			// //////// 然后第三步，finally块，处理AfterAdvice链 //////////
			processAdviceChain(adviceChain.getAfterAdvices(), AdviceProcessor.AFTER_ADVICE_PROCESSOR, null,// 不需要otherParam
																																																		 // ，
																																																		 // 传null
					obj, pointcut, args);
		}
		
		/*
		if(logger.isDebugEnabled()) {
			logger.debug("方法 ["+pointcut.toGenericString()+"] 正常返回，激活 AfterReturningAdvice 链:"+afterAdvices);
		}
		 */
		
		// //////// 最后，如果方法正常返回，处理AfterReturningAdvice链 //////////
		processAdviceChain(adviceChain.getAfterReturningAdvices(),AdviceProcessor.AFTER_RETURN_ADVICE_PROCESSOR,
				returnValue, obj, pointcut, args);

		return returnValue;
	}
	
	/**
	 * 处理 Advice 表。
	 * 
	 * @param <T>
	 *          Advice类型
	 * @param adviceList
	 *          Advice表
	 * @param adviceProcessor
	 *          对应的Advice处理器
	 * @param otherParam
	 *          处理Advice可能用到的其他参数
	 * @param obj
	 *          被advice的对象
	 * @param pointcut
	 *          连接点(被拦截的方法)
	 * @param args
	 *          方法参数
	 */
	private static <T extends Advice> void processAdviceChain(
			List<Class<? extends T>> adviceList,AdviceProcessor adviceProcessor,
			Object otherParam,Object obj,Method pointcut,Object... args) {
		for(Class<? extends T> adviceClass:adviceList) {
			Advice advice=cacheAdvice(adviceClass);
			createLogErrorAdviceProcessor(adviceProcessor).// 用代理包装一下，自动处理错误日志
				processAdvice(otherParam, advice,obj, pointcut, args);
		}
	}
	
}
